From 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b Mon Sep 17 00:00:00 2001 From: Fuwn <50817549+Fuwn@users.noreply.github.com> Date: Sat, 24 Jan 2026 13:09:50 +0000 Subject: Initial commit Created from https://vercel.com/new --- .../websites/[websiteId]/realtime/RealtimeLog.tsx | 206 +++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx (limited to 'src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx') diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx new file mode 100644 index 0000000..1076361 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -0,0 +1,206 @@ +import { Column, Heading, IconLabel, Row, SearchField, Text } from '@umami/react-zen'; +import Link from 'next/link'; +import { useMemo, useState } from 'react'; +import { FixedSizeList } from 'react-window'; +import { SessionModal } from '@/app/(main)/websites/[websiteId]/sessions/SessionModal'; +import { useFormat } from '@/components//hooks/useFormat'; +import { Avatar } from '@/components/common/Avatar'; +import { Empty } from '@/components/common/Empty'; +import { + useCountryNames, + useLocale, + useMessages, + useMobile, + useNavigation, + useTimezone, + useWebsite, +} from '@/components/hooks'; +import { Eye, User } from '@/components/icons'; +import { FilterButtons } from '@/components/input/FilterButtons'; +import { Lightning } from '@/components/svg'; +import { BROWSERS, OS_NAMES } from '@/lib/constants'; + +const TYPE_ALL = 'all'; +const TYPE_PAGEVIEW = 'pageview'; +const TYPE_SESSION = 'session'; +const TYPE_EVENT = 'event'; + +const icons = { + [TYPE_PAGEVIEW]: , + [TYPE_SESSION]: , + [TYPE_EVENT]: , +}; + +export function RealtimeLog({ data }: { data: any }) { + const website = useWebsite(); + const [search, setSearch] = useState(''); + const { formatMessage, labels, messages, FormattedMessage } = useMessages(); + const { formatValue } = useFormat(); + const { locale } = useLocale(); + const { formatTimezoneDate } = useTimezone(); + const { countryNames } = useCountryNames(locale); + const [filter, setFilter] = useState(TYPE_ALL); + const { updateParams } = useNavigation(); + const { isPhone } = useMobile(); + + const buttons = [ + { + label: formatMessage(labels.all), + id: TYPE_ALL, + }, + { + label: formatMessage(labels.views), + id: TYPE_PAGEVIEW, + }, + { + label: formatMessage(labels.visitors), + id: TYPE_SESSION, + }, + { + label: formatMessage(labels.events), + id: TYPE_EVENT, + }, + ]; + + const getTime = ({ createdAt, firstAt }) => formatTimezoneDate(firstAt || createdAt, 'pp'); + + const getIcon = ({ __type }) => icons[__type]; + + const getDetail = (log: { + __type: string; + eventName: string; + urlPath: string; + browser: string; + os: string; + country: string; + device: string; + }) => { + const { __type, eventName, urlPath, browser, os, country, device } = log; + + if (__type === TYPE_EVENT) { + return ( + {eventName || formatMessage(labels.unknown)}, + url: ( + + {urlPath} + + ), + }} + /> + ); + } + + if (__type === TYPE_PAGEVIEW) { + return ( + + {urlPath} + + ); + } + + if (__type === TYPE_SESSION) { + return ( + {countryNames[country] || formatMessage(labels.unknown)}, + browser: {BROWSERS[browser]}, + os: {OS_NAMES[os] || os}, + device: {formatMessage(labels[device] || labels.unknown)}, + }} + /> + ); + } + }; + + const TableRow = ({ index, style }) => { + const row = logs[index]; + return ( + + + + + + + + {getTime(row)} + + + + {getDetail(row)} + + + + ); + }; + + const logs = useMemo(() => { + if (!data) { + return []; + } + + let logs = data.events; + + if (search) { + logs = logs.filter(({ eventName, urlPath, browser, os, country, device }) => { + return [ + eventName, + urlPath, + os, + formatValue(browser, 'browser'), + formatValue(country, 'country'), + formatValue(device, 'device'), + ] + .filter(n => n) + .map(n => n.toLowerCase()) + .join('') + .includes(search.toLowerCase()); + }); + } + + if (filter !== TYPE_ALL) { + return logs.filter(({ __type }) => __type === filter); + } + + return logs; + }, [data, filter, formatValue, search]); + + return ( + + {formatMessage(labels.activity)} + {isPhone ? ( + <> + + + + + + + + ) : ( + + + + + )} + + + {logs?.length === 0 && } + {logs?.length > 0 && ( + + {TableRow} + + )} + + + + ); +} -- cgit v1.2.3